﻿using System;
using System.Threading;
using System.Collections.Generic;
using System.Text;

namespace EnhSimGUI.Solver
{
    /// <summary>
    /// Summary description for Chromosome.
    /// </summary>
    public class Chromosome
    {
        // The cost for the fitness of the chromosome
        protected double cost;
        Random randObj = new Random();
        // The list of priorities which are the genes of the chromosome
        protected int[] priorityList;
        // The mutation rate at percentage.
        protected double mutationPercent;
        // crossover point.
        protected int crossoverPoint;

        public Chromosome(String[] priorities)
        {
            bool[] taken = new bool[priorities.Length];
            priorityList = new int[priorities.Length];
            cost = 0.0;
            for (int i = 0; i < priorityList.Length; i++) taken[i] = false;
            for (int i = 0; i < priorityList.Length - 1; i++)
            {
                int icandidate;
                do
                {
                    icandidate = (int)(randObj.NextDouble() * (double)priorityList.Length);
                } while (taken[icandidate]);
                priorityList[i] = icandidate;
                taken[icandidate] = true;
                if (i == priorityList.Length - 2)
                {
                    icandidate = 0;
                    while (taken[icandidate]) icandidate++;
                    priorityList[i + 1] = icandidate;
                }
            }
            calculateCost(priorities);
            crossoverPoint = 1;
        }

        // fitness calculation
        public void calculateCost(String[] priorities)
        {
            // "rotation_priority_count      " + priorityList.Length
            for (int i = 0; i < priorityList.Length - 1; i++)
            {
                // generate list of priorities in config file 
                // "rotation_priority" + i + "    " + priorities[priorityList[i]]
            }
            // execute sim with that config
            // cost = dps value of that config
            cost = 0.0;
        }

        public double getCost()
        {
            return cost;
        }

        public int getPriority(int i)
        {
            return priorityList[i];
        }

        public void assignPriorities(int[] list)
        {
            for (int i = 0; i < priorityList.Length; i++)
            {
                priorityList[i] = list[i];
            }
        }

        public void assignPriority(int index, int value)
        {
            priorityList[index] = value;
        }

        public void assignCut(int cut)
        {
            crossoverPoint = cut;
        }

        public void assignMutation(double prob)
        {
            mutationPercent = prob;
        }

        public int mate(Chromosome father, Chromosome offspring1, Chromosome offspring2)
        {
            int crossoverPostion1 = (int)((randObj.NextDouble()) * (double)(priorityList.Length - crossoverPoint));
            int crossoverPostion2 = crossoverPostion1 + crossoverPoint;
            int[] offset1 = new int[priorityList.Length];
            int[] offset2 = new int[priorityList.Length];
            bool[] taken1 = new bool[priorityList.Length];
            bool[] taken2 = new bool[priorityList.Length];
            for (int i = 0; i < priorityList.Length; i++)
            {
                taken1[i] = false;
                taken2[i] = false;
            }
            for (int i = 0; i < priorityList.Length; i++)
            {
                if (i < crossoverPostion1 || i >= crossoverPostion2)
                {
                    offset1[i] = -1;
                    offset2[i] = -1;
                }
                else
                {
                    int imother = priorityList[i];
                    int ifather = father.getPriority(i);
                    offset1[i] = ifather;
                    offset2[i] = imother;
                    taken1[ifather] = true;
                    taken2[imother] = true;
                }
            }
            for (int i = 0; i < crossoverPostion1; i++)
            {
                if (offset1[i] == -1)
                {
                    for (int j = 0; j < priorityList.Length; j++)
                    {
                        int imother = priorityList[j];
                        if (!taken1[imother])
                        {
                            offset1[i] = imother;
                            taken1[imother] = true;
                            break;
                        }
                    }
                }
                if (offset2[i] == -1)
                {
                    for (int j = 0; j < priorityList.Length; j++)
                    {
                        int ifather = father.getPriority(j);
                        if (!taken2[ifather])
                        {
                            offset2[i] = ifather;
                            taken2[ifather] = true;
                            break;
                        }
                    }
                }
            }
            for (int i = priorityList.Length - 1; i >= crossoverPostion2; i--)
            {
                if (offset1[i] == -1)
                {
                    for (int j = priorityList.Length - 1; j >= 0; j--)
                    {
                        int imother = priorityList[j];
                        if (!taken1[imother])
                        {
                            offset1[i] = imother;
                            taken1[imother] = true;
                            break;
                        }
                    }
                }
                if (offset2[i] == -1)
                {
                    for (int j = priorityList.Length - 1; j >= 0; j--)
                    {
                        int ifather = father.getPriority(j);
                        if (!taken2[ifather])
                        {
                            offset2[i] = ifather;
                            taken2[ifather] = true;
                            break;
                        }
                    }
                }
            }
            offspring1.assignPriorities(offset1);
            offspring2.assignPriorities(offset2);
            int mutate = 0;
            int swapPoint1 = 0;
            int swapPoint2 = 0;
            if (randObj.NextDouble() < mutationPercent)
            {
                swapPoint1 = (int)(randObj.NextDouble() * (double)(priorityList.Length));
                swapPoint2 = (int)(randObj.NextDouble() * (double)priorityList.Length);
                int i = offset1[swapPoint1];
                offset1[swapPoint1] = offset1[swapPoint2];
                offset1[swapPoint2] = i;
                mutate++;
            }
            if (randObj.NextDouble() < mutationPercent)
            {
                swapPoint1 = (int)(randObj.NextDouble() * (double)(priorityList.Length));
                swapPoint2 = (int)(randObj.NextDouble() * (double)priorityList.Length);
                int i = offset2[swapPoint1];
                offset2[swapPoint1] = offset2[swapPoint2];
                offset2[swapPoint2] = i;
                mutate++;
            }
            return mutate;
        }

        public void PrintPriority(int i, String[] priorities)
        {
            System.Console.WriteLine("rotation_priority" + i.ToString() + "      " + priorities[priorityList[i]]);
        }

        // chromosomes -- an array of chromosomes which is sorted
        // num -- the number of the chromosome list
        public static void sortChromosomes(Chromosome[] chromosomes, int num)
        {
            bool swapped = true;
            Chromosome dummy;
            while (swapped)
            {
                swapped = false;
                for (int i = 0; i < num - 1; i++)
                {
                    if (chromosomes[i].getCost() > chromosomes[i + 1].getCost())
                    {
                        dummy = chromosomes[i];
                        chromosomes[i] = chromosomes[i + 1];
                        chromosomes[i + 1] = dummy;
                        swapped = true;
                    }
                }
            }
        }
    }

}
